package org.go.scheduler.memory;

import java.util.ArrayList;
import java.util.Date;
import java.util.Iterator;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

import org.go.expcetion.GoException;
import org.go.expcetion.JobPersistenceException;
import org.go.expcetion.ObjectAlreadyExistsException;
import org.go.expcetion.SchedulerException;
import org.go.log.Log;
import org.go.log.LoggerFactory;
import org.go.scheduler.AbstractScheduler;
import org.go.scheduler.SchedulerResources;
import org.go.threadpool.GoThreadPool;
import org.go.threadpool.GoThreadPoolRepository;
import org.go.trigger.Calendar;
import org.go.trigger.CalendarContent;
import org.go.trigger.Trigger;
import org.go.trigger.TriggerKey;
import org.go.trigger.TriggerWrapper;
import org.go.work.Work;
import org.go.work.WorkKey;

/**
 * 
 * @author hejie
 *
 */
public class MemoryScheduler extends AbstractScheduler {
	private static final Log log = LoggerFactory.getLog(MemoryScheduler.class);
	private boolean closed = false;
	private GoThreadPool exe = GoThreadPoolRepository.getInstance().lookup();
	private Lock lock = new ReentrantLock();
	private SchedulerResources res;
	//protected long misfireThreshold = 5000l;
	protected ArrayList<TriggerWrapper> triggers = new ArrayList<TriggerWrapper>(1000);

	public MemoryScheduler(SchedulerResources res) throws Exception {
		this.setRes(res);
	}

	private List<TriggerWrapper> getTriggerWrappersForCalendar(String calName) {
		ArrayList<TriggerWrapper> trigList = new ArrayList<TriggerWrapper>();
		synchronized (lock) {
			for (int i = 0; i < triggers.size(); i++) {
				TriggerWrapper tw = triggers.get(i);
				String tcalName = tw.getTrigger().getCalendarName();
				if (tcalName != null && tcalName.equals(calName)) {
					trigList.add(tw);
				}
			}
		}
		return trigList;
	}

	@Override
	public void addCalendar(String calName, Calendar calendar, boolean replaceExisting, boolean updateTriggers) throws SchedulerException {
		try {
			lock.lock();
			calendar = (Calendar) calendar.clone();
			Calendar obj = CalendarContent.getCalendar(calName);

			if (obj != null && replaceExisting == false) {
				throw new ObjectAlreadyExistsException("Calendar with name '" + calName + "' already exists.");
			} else if (obj != null) {
				CalendarContent.remove(calName);
			}

			CalendarContent.setCalendar(calName, calendar);

			if (obj != null && updateTriggers) {
				Iterator<TriggerWrapper> trigs = getTriggerWrappersForCalendar(calName).iterator();
				while (trigs.hasNext()) {
					TriggerWrapper tw = trigs.next();
					Trigger trig = tw.getTrigger();
					trig.updateWithNewCalendar(calendar, 5000L);
					//boolean removed = timeTriggers.remove(tw);
					//if (removed) {
					//timeTriggers.add(tw);
					//}
				}
			}
		} finally {
			lock.unlock();
		}
	}

	/*
	 * (non-Javadoc)
	 * @see org.go.core.Scheduler#getContext()
	 */
	@Override
	public ConcurrentHashMap<String, Object> getContext() {
		return null;
	}

	/*
	 * (non-Javadoc)
	 * @see org.go.core.Scheduler#getJobGroupNames()
	 */
	@Override
	public String getJobGroupNames() {
		return null;
	}

	@Override
	public SchedulerResources getResource() {
		return res;
	}

	/*
	 * (non-Javadoc)
	 * @see org.go.core.Scheduler#getSchedulerName()
	 */
	@Override
	public String getSchedulerName() throws SchedulerException {
		return res.getName();
	}

	@Override
	public Trigger getTrigger(TriggerKey tKey) {
		return null;
	}

	/*
	 * (non-Javadoc)
	 * @see org.go.core.Scheduler#isShutdown()
	 */
	@Override
	public boolean isShutdown() throws SchedulerException {
		return closed;
	}

	/*
	 * (non-Javadoc)
	 * @see org.go.core.Scheduler#notifySchedulerListenersError(java.lang.String, org.go.expcetion.GoException)
	 */
	@Override
	public void notifySchedulerListenersError(String string, GoException se) {

	}

	/*
	 * (non-Javadoc)
	 * @see org.go.core.Scheduler#notifySchedulerListenersError(java.lang.String, org.go.core.JobPersistenceException)
	 */
	@Override
	public void notifySchedulerListenersError(String string, JobPersistenceException se) {

	}

	/*
	 * (non-Javadoc)
	 * @see org.go.Scheduler#pauseAll()
	 */
	@Override
	public void pauseAllWorks() throws SchedulerException {
		exe.pause();
	}

	@Override
	public void pauseWork(WorkKey workKey) throws SchedulerException {
		exe.pause();
	}

	/*
	 * (non-Javadoc)
	 * @see org.go.Scheduler#schedule(org.go.Work, org.go.Trigger)
	 */
	@Override
	public void scheduler(Work work, Trigger trigger) throws SchedulerException {
		try {
			Calendar cal = null;
			if (trigger.getCalendarName() != null) {
				cal = CalendarContent.getCalendar(trigger.getCalendarName());
			}
			Date ft = trigger.computeFirstFireTime(cal);
			if (ft == null) {
				throw new SchedulerException("Based on configured schedule, the given trigger will never fire.");
			}
			exe.scheduleWithCronExp(work, trigger);
		} catch (Exception e) {
			log.error("{}", e);
			throw new SchedulerException(e);
		}
	}

	public void setRes(SchedulerResources res) {
		this.res = res;
	}

	@Override
	public void shutdown(boolean waitForJobsToComplete) throws SchedulerException {
		exe.shutdown(waitForJobsToComplete);
	}

	/*
	 * (non-Javadoc)
	 * @see org.go.core.Scheduler#shutdownNow()
	 */
	@Override
	public void shutdownNow() throws SchedulerException {
		exe.shutdownNow();
		closed = true;
	}

	@Override
	public void unscheduleWork(TriggerKey tKey) throws SchedulerException {

	}

}
